/** 
 * Slots.h
 * @author rwebb
 * @date 30/05/08
 **/

#ifndef VORPAL_SLOTS_H
#define VORPAL_SLOTS_H

#include "Common.h"
#include "Handle.h"
#include <iostream>
#include <list>
#include <vector>
#include <map>
#include <string>
#include <typeinfo>

namespace Vorpal{
	
	class Object;
	class Code;
	class VM;
	
	using std::type_info;

	class SerialMemory{
	public:
		bool Seen(Object *obj, uint *index = 0){
			map<Object*, uint>::iterator found = prevByPtr.find(obj);
			if(found != prevByPtr.end()){
				// found
				if(index){
					*index = found->second;
				}
				return true;
			}
			else{
				prevByPtr[obj] = prevByIndex.size();
				prevByIndex.push_back(obj);
				return false;
			}
		}
		
		Object *Get(uint index){
			Assert(index < prevByIndex.size());
			return prevByIndex[index];
		}
	private:
		map<Object*, uint> prevByPtr;
		vector<Object*> prevByIndex;
	};
		
	/** Slots are where Vorpal objects store subparts.  They are designed to be fast and general purpose.
	 * 
	 * A combination of vectors and maps for fast and sparse associative storage.
	 *	 
	 * 	Slots are implemented to have constant time array access.  
	 * 	The memory overhead for objects with slots is slightly higher 
	 * 	than the old version which just used stl maps.
	 * 
	 * 	nsieve.vorpal with STL maps takes
	 * 		real 0m13.581s
	 * 	with Slots
	 * 		real 0m11.040s
	 * 	which is 20% faster
	 * 
	 * 	using arrays of Booleans via sys.true() and sys.false() gives another 20%
	 * 		real 0m8.879s
	 * 
	 * 	Turing on -O3 optimisation gives
	 * 		real 0m2.989s
	 * 	Which is faster than Perl, TCL, and Ruby.  
	 * 	But still slower than Lua and Python because we operate on high level objects all the time.
	 * 
	 * 	Turing off Asserts gives a small boost in speed but not much.
	 * 
	 * 	Note if you allocate arrays out of order the values are stored in maps and 
	 * 	the time goes up because we are checking for array membership.
	 * 	If you allocated them out of order then the time goes UP to 16.2s (about 20% slower).
	 * 
	 * 	Of course, Vorpal is not designed for low-level number crunching and huge array manipulation.
	 * 
	 **/
	class Slots{
	public:
		virtual ~Slots(){ }
		
		/// returns none if the key has not been stored
		Handle<Object> Get(Message key, VM *vm);
		
		bool HasSlot(Message key){
			return key < arraySlots.size() || mapSlots.find(key) != mapSlots.end();			
		}
				
		virtual void Set(Message key, Handle<Object> value);
		
		void Clear(){
			arraySlots.clear();
			mapSlots.clear();
		}
		
		void CopySlots(Slots &clone) const;

		void AppendTo(Slots &array) const;

		uint ArraySize() const{ return arraySlots.size(); }
		
		void ApplyToSlots(VM *vm, Handle<Code> code, Handle<Object> system, uint numArgs, Handle<Object> *args, bool namedSlots);
		
		void ArchiveSlots(std::ostream &output, VM *vm, SerialMemory &previous);
		void UnArchiveSlots(std::istream &input, VM *vm, SerialMemory &previous);
	private:
		vector< Handle<Object> > arraySlots;
		map< Message, Handle<Object> > mapSlots;
	};
	
}

#endif
